package org.carabiner.infinitest;

import java.io.IOException;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
import java.io.PrintStream;
import java.net.UnknownHostException;
import java.util.ArrayList;
import java.util.List;

public class ExternalProcessTestRunner implements TestRunner
{
  private static final int PORT_NUMBER = 10421;
  private Class<? extends TestRunner> runnerClazz;
  private TestRunnerEventSupport eventSupport;
    
  public ExternalProcessTestRunner(Class<? extends TestRunner> testRunnerClass)
  {
    runnerClazz = testRunnerClass;
    eventSupport = new TestRunnerEventSupport(this);
  }
  
  public void addTestStatusListener(TestStatusListener listener)
  {
    eventSupport.addTestStatusListener(listener);
  }

  public void runTest(String testClass)
  {
    // Check for open socket (aka, running process)
    try
    {      
      String javaExecutable = "java";
      String classpath = System.getProperty("java.class.path");      
      String newProcessClassname = RunnerProcess.class.getName();
      Process process = Runtime.getRuntime().exec(javaExecutable + 
                                                  " -cp " + classpath +
                                                  " " + newProcessClassname +
                                                  " " + runnerClazz.getName() +
                                                  " " + testClass);
             
      ObjectInputStream istream = new ObjectInputStream(process.getInputStream());
      List<TestEvent> eventList = (List<TestEvent>) istream.readObject();
      for (TestEvent event : eventList)
      {
        fireEvent(event);
      }
      istream.close();
      
//      BufferedReader reader = new BufferedReader(new InputStreamReader(istream));      
//      String eventString;
//      do
//      {
//        eventString = reader.readLine();
//        if (eventString != null)
//        {
//          TestEvent event = new TestEvent(this, eventString);
//          fireEvent(event);          
//        }
//      }while (eventString != null);
//      reader.close();     
    }
    catch (IOException e)
    {
      throw new RuntimeException(e);
    }
    catch (ClassNotFoundException e)
    {
      // TODO Auto-generated catch block
      e.printStackTrace();
    }
  }

  private void fireEvent(TestEvent event)
  {
    eventSupport.fireTestEvent(event);    
  }
 
  private static class RunnerProcess implements TestStatusListener
  {    
    private TestRunner runner;    
    private List<TestEvent> eventList;
    
    public RunnerProcess(String runnerClass)
    {
      eventList = new ArrayList<TestEvent>();
      runner = createRunner(runnerClass);      
    }
    
    public void runTests(String testName) throws IOException
    {      
      runner.runTest(testName);
    }

    public void returnResults() throws IOException
    {
      ObjectOutputStream ooStream = new ObjectOutputStream(System.out);
      ooStream.writeObject(eventList);
      ooStream.close();
    }
    
    private TestRunner createRunner(String string)
    {
      TestRunner runner = null;
      try
      {
        Class runnerClass = Class.forName(string);
        runner = (TestRunner) runnerClass.newInstance();
      }
      catch (ClassNotFoundException e)
      {
        throw new RuntimeException(e);
      }
      catch (InstantiationException e)
      {
        throw new RuntimeException(e);
      }
      catch (IllegalAccessException e)
      {
        throw new RuntimeException(e);
      }
      runner.addTestStatusListener(this);
      return runner;
    }

    public void testCaseSucceeded(TestEvent event)
    {    
      processTestEvent(event);
    }

    public void testError(TestEvent event)
    {
      processTestEvent(event);      
    }

    public void testFailed(TestEvent event)
    {
      processTestEvent(event);      
    }

    public void testMethodSucceeded(TestEvent event)
    {
      processTestEvent(event);      
    }

    private void processTestEvent(TestEvent event)
    {      
      eventList.add(event);                  
    }
    
    public static void main(String[] args) throws IOException
    {
      RunnerProcess process = new RunnerProcess(args[0]);
      process.runTests(args[1]);
      process.returnResults();
    }
  }

}
